package cn.xzc.job.cocurrent;

import cn.xzc.job.constant.Constant;
import cn.xzc.job.util.ThrowableUtil;
import jakarta.enterprise.concurrent.ManagedExecutors;
import jakarta.enterprise.concurrent.ManagedTask;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * job执行线程池[异步回调通知+是否是短执行线程]
 *
 * @author xzc
 * 当前时间 2024-02-12 21:30:00
 */
public class ConcurrentJobExecutor implements SchedulingJobExecutor, AsyncListenableJobExecutor {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConcurrentJobExecutor.class);

    /**
     * 异步执行事务Class
     */
    private static Class<?> managedExecutorServiceClass;

    static {
        try {
            managedExecutorServiceClass = Class.forName(
                    Constant.MANAGED_EXECUTOR_SERVICE_CLASS, false,
                    ConcurrentJobScheduler.class.getClassLoader());
        } catch (ClassNotFoundException ex) {
            // JSR-236 API not available...
            managedExecutorServiceClass = null;
            log.error("{}", ThrowableUtil.getThrowableMessage(ex));
        }
    }

    private Executor concurrentExecutor;
    private JobExecutorAdapter adaptedExecutor;

    public ConcurrentJobExecutor() {
        this.concurrentExecutor = Executors.newSingleThreadExecutor();
        this.adaptedExecutor = new JobExecutorAdapter(this.concurrentExecutor);
    }

    /**
     * Create a new ConcurrentJobExecutor, using the given {@link java.util.concurrent.Executor}.
     * <p>Autodetects a JSR-236 {@link jakarta.enterprise.concurrent.ManagedExecutorService}
     * in order to expose {@link jakarta.enterprise.concurrent.ManagedTask} adapters for it.
     *
     * @param executor the {@link java.util.concurrent.Executor} to delegate to
     */
    public ConcurrentJobExecutor( Executor executor) {
        this.concurrentExecutor = (executor != null ? executor : Executors.newSingleThreadExecutor());
        this.adaptedExecutor = getAdaptedExecutor(this.concurrentExecutor);
    }

    private static JobExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
        if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
            return new ManagedJobExecutorAdapter(concurrentExecutor);
        }
        return new JobExecutorAdapter(concurrentExecutor);
    }

    /**
     * Return the {@link java.util.concurrent.Executor} that this adapter delegates to.
     */
    public final Executor getConcurrentExecutor() {
        return this.concurrentExecutor;
    }

    /**
     * Specify the {@link java.util.concurrent.Executor} to delegate to.
     * <p>Autodetects a JSR-236 {@link jakarta.enterprise.concurrent.ManagedExecutorService}
     * in order to expose {@link jakarta.enterprise.concurrent.ManagedTask} adapters for it.
     */
    public final void setConcurrentExecutor( Executor executor) {
        this.concurrentExecutor = (executor != null ? executor : Executors.newSingleThreadExecutor());
        this.adaptedExecutor = getAdaptedExecutor(this.concurrentExecutor);
    }

    /**
     * Specify a custom {@link RunnableDecorator} to be applied to any {@link Runnable}
     * about to be executed.
     * <p>Note that such a decorator is not necessarily being applied to the
     * user-supplied {@code Runnable}/{@code Callable} but rather to the actual
     * execution callback (which may be a wrapper around the user-supplied task).
     * <p>The primary use case is to set some execution context around the task's
     * invocation, or to provide some monitoring/statistics for task execution.
     * <p><b>NOTE:</b> Exception handling in {@code RunnableDecorator} implementations
     * is limited to plain {@code Runnable} execution via {@code execute} calls.
     * In case of {@code #submit} calls, the exposed {@code Runnable} will be a
     * {@code FutureTask} which does not propagate any exceptions; you might
     * have to cast it and call {@code Future#get} to evaluate exceptions.
     */
    public final void setTaskDecorator(RunnableDecorator runnableDecorator) {
        this.adaptedExecutor.setRunnableDecorator(runnableDecorator);
    }

    @Override
    public void execute(Runnable task) {
        this.adaptedExecutor.execute(task);
    }


    @Override
    public Future<?> submit(Runnable task) {
        return this.adaptedExecutor.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return this.adaptedExecutor.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        return this.adaptedExecutor.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        return this.adaptedExecutor.submitListenable(task);
    }

    /**
     * 长时间任务执行和回调通知
     */
    private static class ManagedJobExecutorAdapter extends JobExecutorAdapter {

        public ManagedJobExecutorAdapter(Executor concurrentExecutor) {
            super(concurrentExecutor);
        }

        @Override
        public void execute(Runnable task) {
            super.execute(ManagedTaskBuilder.buildManagedTask(task, task.toString()));
        }

        @Override
        public Future<?> submit(Runnable task) {
            return super.submit(ManagedTaskBuilder.buildManagedTask(task, task.toString()));
        }

        @Override
        public <T> Future<T> submit(Callable<T> task) {
            return super.submit(ManagedTaskBuilder.buildManagedTask(task, task.toString()));
        }

        @Override
        public ListenableFuture<?> submitListenable(Runnable task) {
            return super.submitListenable(ManagedTaskBuilder.buildManagedTask(task, task.toString()));
        }

        @Override
        public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
            return super.submitListenable(ManagedTaskBuilder.buildManagedTask(task, task.toString()));
        }
    }


    /**
     * Delegate that wraps a given Runnable/Callable  with a JSR-236 ManagedTask,
     * exposing a long-running hint based on {@link SchedulingAwareRunnable}
     * and a given identity name.
     */
    protected static class ManagedTaskBuilder {

        public static Runnable buildManagedTask(Runnable task, String identityName) {
            Map<String, String> properties;
            if (task instanceof SchedulingAwareRunnable) {
                properties = new HashMap<>(4);
                properties.put(ManagedTask.LONGRUNNING_HINT,
                        Boolean.toString(((SchedulingAwareRunnable) task).isLongLived()));
            } else {
                properties = new HashMap<>(2);
            }
            properties.put(ManagedTask.IDENTITY_NAME, identityName);
            return ManagedExecutors.managedTask(task, properties, null);
        }

        public static <T> Callable<T> buildManagedTask(Callable<T> task, String identityName) {
            Map<String, String> properties = new HashMap<>(2);
            properties.put(ManagedTask.IDENTITY_NAME, identityName);
            return ManagedExecutors.managedTask(task, properties, null);
        }
    }
}
